///////////////////////////////////////////////////////////////////////////////////////////////////
/// \file deriv_ratio_accumulator.h
///
/// \brief   header file for the accumulator for derivative ratios
///
///////////////////////////////////////////////////////////////////////////////////////////////////

#ifndef PCPS_DERIV_RATIO_ACCUMULATOR_HEADER
#define PCPS_DERIV_RATIO_ACCUMULATOR_HEADER

#include<src/pcps.h>
#include<src/input.h>
#include<src/configuration.h>
#include<src/accumulator.h>

namespace pcps {

  ///////////////////////////////////////////////////////////////////////////////////////////////////
  /// \brief  Class to accumulate the ratios of the values and derivatives of one coefficient
  ///         object to the value of another coefficient object.
  ///
  ///  This class relies on two coefficient objects.  One, referred to as the value function,
  ///  determines the value of the denominator in all the ratios taken.  The other, referred
  ///  to as the derivative function, determines the values of the numerators, which for
  ///  the derivative ratios are the derivative function's derivatives and for the value ratios
  ///  are the derivative function's values.
  ///
  ///////////////////////////////////////////////////////////////////////////////////////////////////
  template <class T> class DerivRatioAccumulator : public pcps::Accumulator<T> {

    private:

      /// \brief  pointer to the function whose value and derivatives are used as numerators
      boost::shared_ptr<pcps::CoeffObj<T> > _der_func;

      /// \brief  pointer to the function whose value is used as the denominator
      boost::shared_ptr<pcps::CoeffObj<T> > _val_func;

      /// \brief  the number of variables to take derivatives with respect to
      int _nderiv;

      /// \brief  a list of the stored derivative ratio data, stored as one set of ratios per configuration sampled
      std::list<std::vector<T> > _der_ratios;

      /// \brief  a list of the stored ratios of the values of the derivative and value functions
      std::list<T> _val_ratios;

    public:

      /// \brief  return the number of derivatives
      int nderiv() const { return _nderiv; }

      /// \brief  return the derivative ratios by reference
      const std::list<std::vector<T> > & der_ratios() const { return _der_ratios; }

      /// \brief  return the value ratios by reference
      const std::list<T> & val_ratios() const { return _val_ratios; }

      ///////////////////////////////////////////////////////////////////////////////////////////////////
      /// \brief  constructs the accumulator
      ///
      /// \param[in]      userinp  the user's input options
      /// \param[in]      der_func a pointer to the function whose derivatives will be used
      /// \param[in]      val_func a pointer to the function whose value will be used
      ///
      ///////////////////////////////////////////////////////////////////////////////////////////////////
      DerivRatioAccumulator(const pcps::Input & userinp,
                            boost::shared_ptr<pcps::CoeffObj<T> > der_func,
                            boost::shared_ptr<pcps::CoeffObj<T> > val_func)
        : Accumulator<T>(),
          _der_func(der_func),
          _val_func(val_func),
          _nderiv(der_func->nfvar()),
          _der_ratios(),
          _val_ratios()
      {}

      ///////////////////////////////////////////////////////////////////////////////////////////////////
      /// \brief  Accumulate the derivative ratios.
      ///
      /// \param[in]      userinp  the user's input options
      /// \param[in]      w        the weight to apply to this configuration's contribution
      /// \param[in]      config   The configuration to accumulate data for.
      ///                          It is assumed that the _der_func and _val_func are initialized
      ///                          for this configuration.
      ///
      ///////////////////////////////////////////////////////////////////////////////////////////////////
      void child_accumulate(const pcps::Input & userinp, const double w, const pcps::Configuration<T> & config) {

        // prepare a new vector of for this configuration's derivatives
        _der_ratios.push_back(std::vector<T>());
        _der_ratios.rbegin()->assign(_nderiv, pcps::zero<T>());

        // compute the derivative ratios and store them in the newly created vector
        const pcps::MantExp<T> x = w / _val_func->get_value();
        _der_func->add_derivatives(config, x, *_der_ratios.rbegin());

        // compute and store the value ratio
        _val_ratios.push_back( ( _der_func->get_value() * x ).get() );

      }

      ///////////////////////////////////////////////////////////////////////////////////////////////////
      /// \brief finalizes the accumulation (this may include for example communicating the results)
      ///
      /// \param[in]      userinp  the user's input options
      ///
      ///////////////////////////////////////////////////////////////////////////////////////////////////
      void child_finalize(const pcps::Input & userinp) {

        // scale the derivative ratios by the total weight
        for (typename std::list<std::vector<T> >::iterator it = _der_ratios.begin(); it != _der_ratios.end(); it++)
          pcps::xscal(_nderiv, pcps::unity<T>() / this->_total_weight, &it->at(0), 1);

        // scale the value ratios by the total weight
        for (typename std::list<T>::iterator it = _val_ratios.begin(); it != _val_ratios.end(); it++)
          *it /= this->_total_weight;

      }

      ///////////////////////////////////////////////////////////////////////////////////////////////////
      /// \brief  resets the accumulator, deleting the store value and derivative ratios
      ///
      ///////////////////////////////////////////////////////////////////////////////////////////////////
      void child_reset() {
        _der_ratios.clear();
        _val_ratios.clear();
      }

  };

}

#endif
